/*   The MIT License
 *   
 *   Carina Shader Effect Compiler
 *   Copyright (c) 2013 Zdravko Velinov
 *   
 *   Permission is hereby granted, free of charge, to any person obtaining a copy
 *   of this software and associated documentation files (the "Software"), to deal
 *   in the Software without restriction, including without limitation the rights
 *   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *   copies of the Software, and to permit persons to whom the Software is
 *   furnished to do so, subject to the following conditions:
 *
 *   The above copyright notice and this permission notice shall be included in
 *   all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *   THE SOFTWARE.
 */

#include "carina/common/exception.hh"
#include "carina/common/parse-command-line.hh"
#include "carina/gl-effect-generator.hh"
#include "carina/dx-effect-generator.hh"
#include "carina/renderer-base.hh"
#include "carina/file-system.hh"
#include "carina/common/system.hh"

#include <cstdlib>
#include <iostream>
#include <fstream>

using namespace Carina;

// TODO: Replace include loaders
class DummyIncludeLoader: public IncludeLoader
{
public:
    virtual bool loadInclude(const string&, const char**, size_t&) override { return true; }
    virtual void freeInclude(const char*) override {}
};

enum CompilerFlags
{
    CompileToGLSL    = 0,
    CompileToHLSL    = 1,
    CompileFlagMask  = 1,
    CompileFlagShift = 0
};

void BuildTextShaderSimple(const string& input_file, std::ostream& output_file, uint32 flags)
{
    auto include_loader = make_aligned_shared<DummyIncludeLoader>();
    Effect::EffectDescription effect;
    int compile_flag = (flags >> CompileFlagShift) & CompileFlagMask;
    if(compile_flag == CompileToGLSL)
    {
        if(!Carina::GLFX::LoadEffect(input_file, include_loader, effect))
            return;
    }
    else if(compile_flag == CompileToHLSL)
    {
        if(!Carina::DXFX::LoadEffect(input_file, include_loader, effect))
            return;
    }
    else
        THROW_EXCEPTION("Unsupported shading language.");
    
    for(size_t i = 0, iend = effect.getShaderCount(); i < iend; ++i)
    {
        auto& shader = effect.getShader(i);
        output_file << "// " << shader.getName() << "\n"
                    << shader.getContents() << std::endl;
    }
}

void BuildTextShaderSimple(const string& input_file, const string& output_file, uint32 flags)
{
    std::fstream fs(output_file.c_str(), std::ios::out);
    if(output_file.empty())
        BuildTextShaderSimple(input_file, std::cout, flags);
    else
        BuildTextShaderSimple(input_file, fs, flags);
}

int CarinaMain(int argc, char* argv[])
{
    CommandLineOptsParser parser("cefx_compiler", true);
    try
    {
        parser.createOption('o', "output", "The name of the file to which the processed shader is going to be written.", true);
        parser.createOption('l', "language", "The shading language of the output file.", true);
        parser.parse(argc, argv);
        string output_file = parser.extract<string>("output");
        string shading_lang = parser.extract<string>("language");
        uint32 flags = 0;
        if(!shading_lang.empty())
        {
            if(shading_lang == "hlsl")
                flags |= (CompileToHLSL << CompileFlagShift);
            else if(shading_lang == "glsl")
                flags |= (CompileToGLSL << CompileFlagShift);
            else
                THROW_EXCEPTION("Unknown shading language: " + shading_lang);
        }
        else
            flags |= (CompileToGLSL << CompileFlagShift);
        
        if(output_file == "-")
            output_file.clear();
        if(parser.getUnassociatedCount() != 1)
            THROW_EXCEPTION("Expecting input file");
        string input_file = parser.getUnassociatedArgument(0);
        BuildTextShaderSimple(input_file, output_file, flags);
    }
    catch(const Carina::Exception& e)
    {
        auto exe_path = System::GetExecutablePath();
        std::cerr << Path(exe_path).filename() << ": error: " << e.what() << "\n\n";
        parser.printHelp(std::cerr);
        std::cerr << std::endl;
    }
    
    return EXIT_SUCCESS;
}
